package com.apobates.forum.trident.controller;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.CacheControl;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.apobates.forum.trident.controller.form.BoardConfigForm;
import com.apobates.forum.member.entity.MemberGroupEnum;
import com.apobates.forum.member.entity.MemberRoleEnum;
import com.apobates.forum.member.storage.core.MemberSessionBean;
import com.apobates.forum.strategy.Strategy;
import com.apobates.forum.strategy.StrategyEntityParam;
import com.apobates.forum.strategy.StrategyMode;
import com.apobates.forum.attention.core.ImagePathCoverter;
import com.apobates.forum.core.ad.ActiveDirectoryConnectorFactory;
import com.apobates.forum.core.api.ImageIOMeta;
import com.apobates.forum.core.api.service.BoardConfigService;
import com.apobates.forum.core.api.service.BoardGroupService;
import com.apobates.forum.core.api.service.BoardModeratorService;
import com.apobates.forum.core.api.service.BoardService;
import com.apobates.forum.core.api.service.BoardStatsService;
import com.apobates.forum.core.api.service.TopicService;
import com.apobates.forum.core.entity.Board;
import com.apobates.forum.core.entity.BoardConfig;
import com.apobates.forum.core.entity.BoardGroup;
import com.apobates.forum.core.entity.BoardStats;
import com.apobates.forum.core.entity.Topic;
import com.apobates.forum.event.elderly.ActionEventCulpritor;
import com.apobates.forum.event.elderly.ForumActionEnum;
import com.apobates.forum.trident.OnlineDescriptor;
import com.apobates.forum.trident.exception.ResourceNotFoundException;
import com.apobates.forum.trident.vo.ForumThreads;
import com.apobates.forum.utils.CommonLink;
import com.apobates.forum.utils.Commons;
import com.apobates.forum.utils.TipMessage;
import com.apobates.forum.utils.CommonLink.CommonLinkBuilder;
import com.apobates.forum.utils.lang.EnumArchitecture;
import com.apobates.forum.utils.persistence.Page;
import com.apobates.forum.utils.persistence.PageRequest;
import com.google.gson.Gson;
/**
 * 版块控制器
 * 
 * @author xiaofanku@live.cn
 * @since 20190306
 */
@Controller
@RequestMapping(value = "/board")
public class BoardController {
	@Autowired
	private BoardGroupService boardGroupService;
	@Autowired
	private BoardConfigService boardConfigService;
	@Autowired
	private BoardService boardService;
	@Autowired
	private BoardModeratorService boardModeratorService;
	@Autowired
	private TopicService topicService;
	@Autowired
	private BoardStatsService boardStatsService;
	@Autowired
	private ImageIOMeta imageIOConfig;
	@Value("${site.domain}")
	private String siteDomain;
	@Value("${site.pageSize}")
	private int pageSize;
	
	@ModelAttribute("boardArg")
	public Board getTopic(HttpServletRequest request){
		return ActiveDirectoryConnectorFactory.parseRefererToBoard(request.getHeader("referer"), siteDomain).orElse(null);
	}
	
	// 版块主页(所有话题)
	@GetMapping(path="/{path}.xhtml")
	@OnlineDescriptor(action=ForumActionEnum.BOARD_BROWSE)
	@Strategy(action=ForumActionEnum.BOARD_BROWSE, param=StrategyEntityParam.URI, mode=StrategyMode.READ)
	public String boardHome(
			@PathVariable("path") String connectValue, 
			@RequestParam(value = "category", required = false, defaultValue = "") String categoryValue, 
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model) {
		Board adObj = ActiveDirectoryConnectorFactory.build(Board.class, connectValue).orElseThrow(()->new ResourceNotFoundException("版块路径参数解析失败"));
		// 版块的统计怎么加载: A是要推(websocket)?用户真的关心这个数据的实时性吗!B是要拉(ajax)?
		Board board = boardService.getBoardStats(adObj.getId()); //有统计数据
		//----------------------------------策略检查开始,不限角色
		//  迁至StrategyInterceptorAdapter
		//----------------------------------策略检查结束
		// 版块下的话题异步加载
		//----------------------------------
		BoardGroup volumes = boardGroupService.get(board.getVolumesId()).orElse(BoardGroup.empty(board.getVolumesId()));
		board.setVolumes(volumes);
		model.addAttribute("board", board);
		model.addAttribute("category", categoryValue);
		return "default/board/view";
	}
	
	// 会员星标版块
	@PostMapping(path = "/star", produces = "application/json;charset=UTF-8")
	@ResponseBody
	@OnlineDescriptor(action=ForumActionEnum.BOARD_FAVORITE, isAjax=true)
	@Strategy(action=ForumActionEnum.BOARD_FAVORITE)
	public String starBoardAction(
			@RequestParam(value = "token", required = false, defaultValue = "0")String token, 
			@ModelAttribute("boardArg")Board adObj, 
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model) {
		//----------------------------------从Http Referer中获取需要的参数
		if(null == adObj || adObj.getId()<1){
			return TipMessage.ofError("操作参数解析失败").toJsonString();
		}
		long boardId = adObj.getId();
		//----------------------------------
		Board board = boardService.get(boardId).orElse(Board.empty(0));
		ActionEventCulpritor aec = ActionEventCulpritor.getInstance(mbean.getMid(), mbean.getNickname(), request, token);
		//----------------------------------策略检查开始,不限角色
		//  迁至StrategyInterceptorAdapter
		//----------------------------------策略检查结束
		try{
			Optional<Boolean> symbol = boardService.favorite(boardId, aec); 
			if (symbol.isPresent()) {
				//缓存需要的@20200330
				Map<String,String> extOutputCacheParames = new HashMap<>();
				extOutputCacheParames.put("cacheSequence", board.getConnect());
				extOutputCacheParames.put("cacheModule", "board");
				extOutputCacheParames.put("cacheAction", "star");
				return TipMessage.ofSuccess("版块收藏成功").toJsonString(extOutputCacheParames);
			}
		}catch(IllegalStateException e){
			return TipMessage.ofError(e.getMessage()).toJsonString();
		}
		return TipMessage.ofError("操作失败或已经收藏").toJsonString();
	}
	// 是否可以进行星标版块
	@GetMapping(path = "/star/check", produces = "application/json;charset=UTF-8")
	@ResponseBody
	public String initStarBoardStatus(
			@ModelAttribute("boardArg")Board adObj, 
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model){
		//-------------------------------------------------从Http Referer中获取需要的参数
		if(null == adObj || adObj.getId()<1){
			return "{}";
		}
		Map<String,String> statsData = new HashMap<>();
		if(mbean.isOnline()){
			boolean continueAction = boardService.isFavorited(adObj.getId(), mbean.getMid());
			statsData.put("favorited", continueAction?"1":"0"); //是否可以收藏
			statsData.put("victim", mbean.getMid()+"");
			statsData.put("action", continueAction?"true":"false");
			//缓存需要的@20200330
			statsData.put("cacheSequence", adObj.getConnect());
			statsData.put("cacheModule", "board");
			statsData.put("cacheAction", "star");
		}else{ //匿名不可以
			statsData.put("favorited", "0"); 
			statsData.put("victim", "0"); 
			statsData.put("action", "true"); //按钮是否可用
		}
		return Commons.toJson(statsData);
	}
	// 删除星标的版块
	@PostMapping(path = "/star/cancel", produces = "application/json;charset=UTF-8")
	@ResponseBody
	@OnlineDescriptor(action=ForumActionEnum.BOARD_FAVORITE_CANCEL, isAjax=true)
	@Strategy(action=ForumActionEnum.BOARD_FAVORITE_CANCEL, param=StrategyEntityParam.QUERY_STR, paramId="id")
	public String undoStarBoardAction(
			@RequestParam(value = "token", required = false, defaultValue = "0")String token, 
			@RequestParam("id")long boardId, 
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model){
		if(boardId<1 || !mbean.isOnline()){
			return TipMessage.ofError("参数丢失或不合法").toJsonString();
		}
		Board board = boardService.get(boardId).orElse(Board.empty(0));
		ActionEventCulpritor aec = ActionEventCulpritor.getInstance(mbean.getMid(), mbean.getNickname(), request, token);
		//----------------------------------策略检查开始,不限角色
		//  迁至StrategyInterceptorAdapter
		//----------------------------------策略检查结束
		try{
			Optional<Boolean> symbol = boardService.removeFavorite(boardId, aec); 
			if (symbol.isPresent()) {
				//缓存需要的@20200330
				Map<String,String> extOutputCacheParames = new HashMap<>();
				extOutputCacheParames.put("cacheSequence", board.getConnect());
				extOutputCacheParames.put("cacheModule", "board");
				extOutputCacheParames.put("cacheAction", "star");
				extOutputCacheParames.put("cacheValue", "true");
				return TipMessage.ofSuccess("已成功取消收藏").toJsonString(extOutputCacheParames);
			}
		}catch(IllegalStateException e){
			TipMessage.ofError(e.getMessage()).toJsonString();
		}
		return TipMessage.ofError("操作失败或已取消收藏").toJsonString();
	}
	// 批量删除星标的版块
	@PostMapping(path = "/star/removes", produces = "application/json;charset=UTF-8")
	@ResponseBody
	@OnlineDescriptor(action=ForumActionEnum.BOARD_FAVORITE_CANCEL, isAjax=true)
	public String batchUndoStarBoardAction(
			@RequestParam(value = "token", required = false, defaultValue = "0")String token, 
			@RequestParam("ids")String boardConnectString, 
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model){
		Map<Long,String> boardConnectMap = parseRemoveStarBoards(boardConnectString);
		if(boardConnectMap.isEmpty() || !mbean.isOnline()){
			return TipMessage.ofError("参数丢失或不合法").toJsonString();
		}
		//
		ActionEventCulpritor aec = ActionEventCulpritor.getInstance(mbean.getMid(), mbean.getNickname(), request, token);
		List<Long> affectIds = boardService.removeFavorites(boardConnectMap.keySet(), aec); 
		if (affectIds.size() > 0) {
			Map<String,String> data = new HashMap<>();
			data.put("message", "已成功取消收藏");
			data.put("level", "acc");
			data.put("affect", String.join(",", collectRemoveStarBoardConnects(boardConnectMap, affectIds))); //?
			//缓存需要的@20200330
			data.put("cacheModule", "board");
			data.put("cacheAction", "star");
			data.put("cacheValue", "true");
			return Commons.toJson(data);
		}
		return TipMessage.ofError("操作失败或已取消收藏").toJsonString();
	}
	private Map<Long,String> parseRemoveStarBoards(String boardConnectString){
		Map<Long, String> data = new HashMap<>();
		for(String connect : boardConnectString.split(",")){
			if(!Commons.isNotBlank(connect) || !connect.contains("-")){
				continue;
			}
			Long boardId = 0L;String tmp = connect.replace("+", "").trim();
			try{
				boardId = Long.valueOf(tmp.substring(0, tmp.indexOf("-")));
			}catch(Exception e){}
			if(boardId > 0){
				data.put(boardId, tmp);
			}
		}
		return data;
	}
	private Set<String> collectRemoveStarBoardConnects(Map<Long,String> boardConnectMap, List<Long> affectBoardIdSet){
		return boardConnectMap.entrySet().stream().filter(e-> affectBoardIdSet.contains(e.getKey())).map(Map.Entry::getValue).collect(Collectors.toSet());
	}
	// 锁定
	@PostMapping(path="/lock", produces = "application/json;charset=UTF-8")
	@ResponseBody
	@OnlineDescriptor(action=ForumActionEnum.BOARD_LOCK, isAjax=true)
	@Strategy(action=ForumActionEnum.BOARD_LOCK, allowRoles={MemberRoleEnum.ADMIN, MemberRoleEnum.MASTER, MemberRoleEnum.BM})
	public TipMessage lockBoardAction(
			@RequestParam(value = "token", required = false, defaultValue = "0")String token, 
			@ModelAttribute("boardArg")Board adObj, 
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model) {
		//----------------------------------从Http Referer中获取需要的参数
		if(null==adObj || adObj.getId()<1){
			return TipMessage.ofError("操作参数解析失败");
		}
		long boardId = adObj.getId(); int boardGroupId = adObj.getVolumesId();
		//----------------------------------
		ActionEventCulpritor aec = ActionEventCulpritor.getInstance(mbean.getMid(), mbean.getNickname(), request, token);
		//----------------------------------策略检查开始:只有管理员可以锁定
		//  迁至StrategyInterceptorAdapter
		//----------------------------------策略检查结束
		boolean symbol = boardService.lock(boardId, boardGroupId, aec).orElse(false); 
		if (symbol) {
			return TipMessage.ofSuccess("版块成功锁定");
		}
		return TipMessage.ofError("操作失败");
	}
	
	// 解锁
	@PostMapping(path="/unlock", produces = "application/json;charset=UTF-8")
	@ResponseBody
	@OnlineDescriptor(action=ForumActionEnum.BOARD_UNLOCK, isAjax=true)
	@Strategy(action=ForumActionEnum.BOARD_UNLOCK, allowRoles={MemberRoleEnum.ADMIN, MemberRoleEnum.MASTER, MemberRoleEnum.BM})
	public TipMessage unlockBoardAction(
			@RequestParam(value = "token", required = false, defaultValue = "0")String token, 
			@ModelAttribute("boardArg")Board adObj, 
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model) {
		//----------------------------------从Http Referer中获取需要的参数
		if(null==adObj || adObj.getId()<1){
			return TipMessage.ofError("操作参数解析失败");
		}
		long boardId = adObj.getId(); int boardGroupId = adObj.getVolumesId();
		//----------------------------------
		ActionEventCulpritor aec = ActionEventCulpritor.getInstance(mbean.getMid(), mbean.getNickname(), request, token);
		//----------------------------------策略检查开始:只有管理员可以解锁
		//  迁至StrategyInterceptorAdapter
		//----------------------------------策略检查结束
		boolean symbol = boardService.releaseLock(boardId, boardGroupId, aec).orElse(false); 
		if (symbol) {
			return TipMessage.ofSuccess("版块解锁成功");
		}
		return TipMessage.ofError("操作失败");
	}
	
	// 编辑版块的配置
	@GetMapping(path="/config/edit")
	@OnlineDescriptor(action=ForumActionEnum.BOARD_CONFIG_EDIT)
	@Strategy(action=ForumActionEnum.BOARD_CONFIG_EDIT, allowRoles={MemberRoleEnum.ADMIN, MemberRoleEnum.MASTER, MemberRoleEnum.BM})
	public String boardConfigForm(
			@RequestParam(value = "token", required = false, defaultValue = "0")String token, 
			@ModelAttribute("boardArg")Board adObj, 
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model) {
		//----------------------------------从Http Referer中获取需要的参数
		if(null==adObj || adObj.getId()<1){
			throw new ResourceNotFoundException("操作参数解析失败");
		}
		long boardId = adObj.getId(); int boardGroupId = adObj.getVolumesId();
		//----------------------------------
		Board board = boardService.get(boardId).orElse(Board.empty(0));
		//----------------------------------策略检查开始,限制角色:管理员,版主,大版主都可以编辑
		//  迁至StrategyInterceptorAdapter
		//----------------------------------策略检查结束
		BoardConfigForm form = new BoardConfigForm();
		BoardConfig config = boardConfigService.getByBoardId(boardId).orElse(BoardConfig.defaultConfig(boardId));
		//boolean
		form.setReadWrite(config.isReadWrite());
		form.setIpFilter(config.isIpFilter());
		//读
		form.setReadMinScore(config.getReadMinScore()+"");
		form.setReadLowMemberGroup(config.getReadLowMemberGroup().getSymbol()+"");
		form.setReadLowMemberRole(config.getReadLowMemberRole().getSymbol()+"");
		form.setReadLowMemberLevel(config.getReadLowMemberLevel()+"");
		//写
		form.setEditMinute(config.getEditMinute()+"");
		form.setWriteMinInterrupt(config.getWriteMinInterrupt()+"");
		form.setWriteMinScore(config.getWriteMinScore()+"");
		form.setWriteLowMemberGroup(config.getWriteLowMemberGroup().getSymbol()+"");
		form.setWriteLowMemberRole(config.getWriteLowMemberRole().getSymbol()+"");
		form.setWriteLowMemberLevel(config.getWriteLowMemberLevel()+"");
		//
		form.setRecord(config.getId());
		form.setBoardId(boardId+"");
		form.setVolumesId(boardGroupId+"");
		//
		form.setToken(token); 
		model.addAttribute("form", form);
		model.addAttribute("board", board);
		//
		return "default/board/config";
	}
	@PostMapping(path="/config/edit")
	@OnlineDescriptor(action=ForumActionEnum.BOARD_CONFIG_EDIT)
	@Strategy(action=ForumActionEnum.BOARD_CONFIG_EDIT, param=StrategyEntityParam.QUERY_STR, paramId="boardId", allowRoles={MemberRoleEnum.ADMIN, MemberRoleEnum.MASTER, MemberRoleEnum.BM})
	public String boardConfigAction(@ModelAttribute("form")BoardConfigForm form, MemberSessionBean mbean, HttpServletRequest request, Model model) {
		Board board = boardService.get(form.getLongBoardId()).orElse(Board.empty(0));
		ActionEventCulpritor aec = ActionEventCulpritor.getInstance(mbean.getMid(), mbean.getNickname(), request, form.getToken());
		//----------------------------------策略检查开始, 限制角色:管理员,版主,大版主都可以编辑
		//  迁至StrategyInterceptorAdapter
		//----------------------------------策略检查结束
		BoardConfig config = new BoardConfig();
		//boolean
		config.setReadWrite(form.getBooleanReadWrite());
		config.setIpFilter(form.getBooleanIpFileter());
		//读
		config.setReadMinScore(form.getIntegerReadMinScore());
		MemberGroupEnum rmg = (MemberGroupEnum)EnumArchitecture.getInstance(form.getIntegerReadLowMemberGroup(), MemberGroupEnum.class).orElse((EnumArchitecture)MemberGroupEnum.GUEST);
		config.setReadLowMemberGroup(rmg);
		MemberRoleEnum rmr = (MemberRoleEnum)EnumArchitecture.getInstance(form.getIntegerReadLowMemberRole(), MemberRoleEnum.class).orElse((EnumArchitecture)MemberRoleEnum.NO);
		config.setReadLowMemberRole(rmr);
		config.setReadLowMemberLevel(form.getIntegerReadLowMemberLevel());
		//写
		config.setEditMinute(form.getIntegerEditMinute());
		config.setWriteMinInterrupt(form.getIntegerWriteMinInterrupt());
		config.setWriteMinScore(form.getIntegerWriteMinScore());
		MemberGroupEnum wmg = (MemberGroupEnum)EnumArchitecture.getInstance(form.getIntegerWriteLowMemberGroup(), MemberGroupEnum.class).orElse((EnumArchitecture)MemberGroupEnum.CARD);
		config.setWriteLowMemberGroup(wmg);
		MemberRoleEnum wmr = (MemberRoleEnum)EnumArchitecture.getInstance(form.getIntegerWriteLowMemberRole(), MemberRoleEnum.class).orElse((EnumArchitecture)MemberRoleEnum.NO);
		config.setWriteLowMemberRole(wmr);
		config.setWriteLowMemberLevel(form.getIntegerWriteLowMemberLevel());
		//
		Optional<Boolean> symbol = boardService.editBoardConfig(
				form.getLongBoardId(), 
				form.getIntegerVolumesId(), 
				form.getLongRecord(), 
				config, 
				aec);
		if(symbol.isPresent()) {
			return String.format("redirect:/board/%s.xhtml", ActiveDirectoryConnectorFactory.generateBoardConnectString(form.getIntegerVolumesId(), form.getLongBoardId()));
		}
		model.addAttribute("form", form);
		model.addAttribute("board", board);
		//
		model.addAttribute("errors", "更新版块下的话题策略配置失败");
		//
		return "default/board/config";
	}
	
	// 查看指定版块的图标
	@GetMapping(path = "/ico/{id}.png", produces = MediaType.IMAGE_PNG_VALUE)
	public ResponseEntity<byte[]> getBoardIco(
			@PathVariable("id") long id, 
			HttpServletResponse response, 
			Model model) {
		Board board = boardService.get(id).orElse(null);
		if(null == board){
			response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
			return null;
		}
		try {
			String boardIcoURL = new ImagePathCoverter(board.getImageAddr())
								.decodeUploadImageFilePath(imageIOConfig.getImageBucketDomain(), imageIOConfig.getUploadImageDirectName())
								.orElse("/board/ico/default_icon.png");
			InputStream in = new URL(boardIcoURL).openStream();
			byte[] avtarBytes = IOUtils.toByteArray(in);

			final HttpHeaders headers = new HttpHeaders();
			headers.setContentType(MediaType.IMAGE_PNG);
			headers.setCacheControl(CacheControl.noCache().getHeaderValue());
			return new ResponseEntity<byte[]>(avtarBytes, headers, HttpStatus.CREATED);
		} catch (IOException e) {
			response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
			return null;
		}
	}
	
	// 工具菜单
	@GetMapping(path = "/tool.jsonp", produces = "application/javascript;charset=UTF-8")
	@ResponseBody
	public String getBoardToolMenus( 
			@RequestParam("callback") String callBackFun, 
			@RequestParam(value = "box", required = false, defaultValue = "null") String boxEle, 
			@ModelAttribute("boardArg")Board adObj, 
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model){
		//-------------------------------------------------从Http Referer中获取需要的参数
		if(null==adObj || adObj.getId()<1){
			return callBackFun + "({});";
		}
		long boardId = adObj.getId(); int boardGroupId = adObj.getVolumesId();
		//-------------------------------------------------
		Map<String, List<CommonLink>> menues = new HashMap<>();
		List<CommonLink> publicLink = Arrays.asList(
				CommonLinkBuilder.setAnchor("收藏").setLink("/board/star").isAjax(true).build(), 
				CommonLinkBuilder.setAnchor("订阅").setLink("/rss/board.xml?id="+boardId+"&volumes="+boardGroupId).build(), 
				CommonLinkBuilder.setAnchor("文本").build(), 
				CommonLinkBuilder.setAnchor("打印").build());
		menues.put("public", publicLink);
		if(MemberRoleEnum.ADMIN == mbean.getRole() || boardModeratorService.get(boardGroupId, boardId, mbean.getMid()).isPresent()){
			List<CommonLink> manageLink = Arrays.asList(
					CommonLinkBuilder.setAnchor("锁定").setLink("/board/lock").isAjax(true).build(),
					CommonLinkBuilder.setAnchor("解锁").setLink("/board/unlock").isAjax(true).build(), 
					CommonLinkBuilder.setAnchor("配置").setLink("/board/config/edit").build());
			menues.put("manager", manageLink);
		}
		Map<String, Object> result = new HashMap<>();
		result.put("element", boxEle);
		result.put("result", menues);
		Gson gson = new Gson();
		return callBackFun + "(" + gson.toJson(result) + ");";
	}
	
	//异步加载指定版块下的话题
	@GetMapping(path = "/threads.json", produces = "application/json;charset=UTF-8")
	@ResponseBody
	public String getBoardTopics(
			@RequestParam("pageNumber")int page, 
			@RequestParam("pageSize")int pageSize, 
			@RequestParam(value = "category", required = false, defaultValue = "") String categoryValue, 
			@ModelAttribute("boardArg")Board adObj, 
			HttpServletRequest request, 
			Model model) {
		//-------------------------------------------------从Http Referer中获取需要的参数
		if(null==adObj || adObj.getId()<1){
			return "{}";
		}
		//-------------------------------------------------
		Page<Topic> rs = topicService.getAll(adObj.getId(), categoryValue, new PageRequest(page, pageSize));
		List<ForumThreads> data = rs.getResult().map(ForumThreads::new).collect(Collectors.toList());
		Map<String,Object> result = new HashMap<>();
		result.put("result", data);
		result.put("total", rs.getTotalElements());
		result.put("board", adObj.getId());
		result.put("volumes", adObj.getVolumesId());
		result.put("page", page);
		result.put("size", pageSize);
		if(Commons.isNotBlank(categoryValue)){
			result.put("category", categoryValue);
		}
		Gson gson = new Gson();
		return gson.toJson(result);
	}
	
	//异步加载话题的统计
	@GetMapping(path = "/stats.json", produces = "application/json;charset=UTF-8")
	@ResponseBody
	public String getBoardStats(
			@ModelAttribute("boardArg")Board adObj, 
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model){
		//-------------------------------------------------从Http Referer中获取需要的参数
		if(null==adObj || adObj.getId()<1){
			return "{}";
		}
		//-------------------------------------------------
		BoardStats bs = boardStatsService.getByBoard(adObj.getId()).orElse(new BoardStats(adObj.getId(), adObj.getVolumesId()));
		Map<String,Long> statsData = bs.statsMap();
		
		if(mbean.isOnline()){
			statsData.put("favorited", boardService.isFavorited(adObj.getId(), mbean.getMid())?1L:0L); //是否可以收藏
			statsData.put("victim", mbean.getMid()); 
		}else{ //匿名不可以
			statsData.put("favorited", 0L); 
			statsData.put("victim", 0L); 
		}
		return new Gson().toJson(statsData);
	}
}